/**
 * 
 */
package org.wolf.cluster.util;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.zip.CRC32;

/**
 * Hash算法大全<br>
 * 推荐使用FNV1算法
 * 
 */
public abstract class HashUtil {

	/**
	 * MASK值，随便找一个值，最好是质数
	 */
	private static int M_MASK = 0x8765fed1;

	// 32位FNV算法
	private static int M_SHIFT = 0;

	/**
	 * 加法hash
	 * 
	 * @param key
	 *            字符串
	 * @param prime
	 *            一个质数
	 * @return hash结果
	 */
	public static int additiveHash(String key, int prime) {
		int hash, i;
		for (hash = key.length(), i = 0; i < key.length(); i++)
			hash += key.charAt(i);
		return (hash % prime);
	}

	/**
	 * 旋转hash
	 * 
	 * @param key
	 *            输入字符串
	 * @param prime
	 *            质数
	 * @return hash值
	 */
	public static int rotatingHash(String key, int prime) {
		int hash, i;
		for (hash = key.length(), i = 0; i < key.length(); ++i)
			hash = (hash << 4) ^ (hash >> 28) ^ key.charAt(i);
		return (hash % prime);
	}

	/**
	 * 一次一个hash
	 * 
	 * @param key
	 *            输入字符串
	 * @return 输出hash值
	 */
	public static int oneByOneHash(String key) {
		int hash, i;
		for (hash = 0, i = 0; i < key.length(); ++i) {
			hash += key.charAt(i);
			hash += (hash << 10);
			hash ^= (hash >> 6);
		}
		hash += (hash << 3);
		hash ^= (hash >> 11);
		hash += (hash << 15);
		return hash;
	}

	/**
	 * Bernstein's hash
	 * 
	 * @param key
	 *            输入字节数组
	 * @param level
	 *            初始hash常量
	 * @return 结果hash
	 */
	public static int bernstein(String key) {
		int hash = 0;
		int i;
		for (i = 0; i < key.length(); ++i)
			hash = 33 * hash + key.charAt(i);
		return hash;
	}

	/**
	 * Universal Hashing
	 */
	public static int universal(char[] key, int mask, int[] tab) {
		int hash = key.length, i, len = key.length;
		for (i = 0; i < (len << 3); i += 8) {
			char k = key[i >> 3];
			if ((k & 0x01) == 0)
				hash ^= tab[i + 0];
			if ((k & 0x02) == 0)
				hash ^= tab[i + 1];
			if ((k & 0x04) == 0)
				hash ^= tab[i + 2];
			if ((k & 0x08) == 0)
				hash ^= tab[i + 3];
			if ((k & 0x10) == 0)
				hash ^= tab[i + 4];
			if ((k & 0x20) == 0)
				hash ^= tab[i + 5];
			if ((k & 0x40) == 0)
				hash ^= tab[i + 6];
			if ((k & 0x80) == 0)
				hash ^= tab[i + 7];
		}
		return (hash & mask);
	}

	/**
	 * Zobrist Hashing
	 */
	public static int zobrist(char[] key, int mask, int[][] tab) {
		int hash, i;
		for (hash = key.length, i = 0; i < key.length; ++i)
			hash ^= tab[i][key[i]];
		return (hash & mask);
	}

	/**
	 * 32位的FNV算法
	 * 
	 * @param key
	 *            数组
	 * @return int值
	 */
	public static int FNVHash(byte[] key) {
		int hash = (int) 2166136261L;
		for (byte b : key)
			hash = (hash * 16777619) ^ b;
		if (M_SHIFT == 0)
			return hash;
		return (hash ^ (hash >> M_SHIFT)) & M_MASK;
	}

	/**
	 * 改进的32位FNV算法1
	 * 
	 * @param key
	 *            数组
	 * @return int值
	 */
	public static int FNVHash1(byte[] key) {
		final int p = 16777619;
		int hash = (int) 2166136261L;
		for (byte b : key)
			hash = (hash ^ b) * p;
		hash += hash << 13;
		hash ^= hash >> 7;
		hash += hash << 3;
		hash ^= hash >> 17;
		hash += hash << 5;
		return hash;
	}

	/**
	 * 改进的32位FNV算法1
	 * 
	 * @param key
	 *            字符串
	 * @return int值
	 */
	public static int FNVHash1(String key) {
		final int p = 16777619;
		int hash = (int) 2166136261L;
		for (int i = 0; i < key.length(); i++)
			hash = (hash ^ key.charAt(i)) * p;
		hash += hash << 13;
		hash ^= hash >> 7;
		hash += hash << 3;
		hash ^= hash >> 17;
		hash += hash << 5;
		return hash;
	}

	/**
	 * Thomas Wang的算法，整数hash
	 */
	public static int intHash(int key) {
		key += ~(key << 15);
		key ^= (key >>> 10);
		key += (key << 3);
		key ^= (key >>> 6);
		key += ~(key << 11);
		key ^= (key >>> 16);
		return key;
	}

	/**
	 * RS算法hash
	 * 
	 * @param key
	 *            字符串
	 */
	public static int RSHash(String key) {
		int b = 378551;
		int a = 63689;
		int hash = 0;

		for (int i = 0; i < key.length(); i++) {
			hash = hash * a + key.charAt(i);
			a = a * b;
		}

		return (hash & 0x7FFFFFFF);
	}

	/**
	 * JS算法
	 */
	public static int JSHash(String key) {
		int hash = 1315423911;

		for (int i = 0; i < key.length(); i++) {
			hash ^= ((hash << 5) + key.charAt(i) + (hash >> 2));
		}

		return (hash & 0x7FFFFFFF);
	}

	/**
	 * PJW算法
	 */
	public static int PJWHash(String key) {
		int BitsInUnsignedInt = 32;
		int ThreeQuarters = (BitsInUnsignedInt * 3) / 4;
		int OneEighth = BitsInUnsignedInt / 8;
		int HighBits = 0xFFFFFFFF << (BitsInUnsignedInt - OneEighth);
		int hash = 0;
		int test = 0;

		for (int i = 0; i < key.length(); i++) {
			hash = (hash << OneEighth) + key.charAt(i);

			if ((test = hash & HighBits) != 0) {
				hash = ((hash ^ (test >> ThreeQuarters)) & (~HighBits));
			}
		}

		return (hash & 0x7FFFFFFF);
	}

	/**
	 * ELF算法
	 */
	public static int ELFHash(String key) {
		int hash = 0;
		int x = 0;

		for (int i = 0; i < key.length(); i++) {
			hash = (hash << 4) + key.charAt(i);
			if ((x = (int) (hash & 0xF0000000L)) != 0) {
				hash ^= (x >> 24);
				hash &= ~x;
			}
		}

		return (hash & 0x7FFFFFFF);
	}

	/**
	 * BKDR算法
	 */
	public static int BKDRHash(String key) {
		int seed = 131; // 31 131 1313 13131 131313 etc..
		int hash = 0;

		for (int i = 0; i < key.length(); i++) {
			hash = (hash * seed) + key.charAt(i);
		}

		return (hash & 0x7FFFFFFF);
	}

	/**
	 * SDBM算法
	 */
	public static int SDBMHash(String key) {
		int hash = 0;

		for (int i = 0; i < key.length(); i++) {
			hash = key.charAt(i) + (hash << 6) + (hash << 16) - hash;
		}

		return (hash & 0x7FFFFFFF);
	}

	/**
	 * DJB算法
	 */
	public static int DJBHash(String key) {
		int hash = 5381;

		for (int i = 0; i < key.length(); i++) {
			hash = ((hash << 5) + hash) + key.charAt(i);
		}

		return (hash & 0x7FFFFFFF);
	}

	/**
	 * DEK算法
	 */
	public static int DEKHash(String key) {
		int hash = key.length();

		for (int i = 0; i < key.length(); i++) {
			hash = ((hash << 5) ^ (hash >> 27)) ^ key.charAt(i);
		}

		return (hash & 0x7FFFFFFF);
	}

	/**
	 * AP算法
	 */
	public static int APHash(String key) {
		int hash = 0;

		for (int i = 0; i < key.length(); i++) {
			hash ^= ((i & 1) == 0) ? ((hash << 7) ^ key.charAt(i) ^ (hash >> 3))
					: (~((hash << 11) ^ key.charAt(i) ^ (hash >> 5)));
		}

		// return (hash & 0x7FFFFFFF);
		return hash;
	}

	/**
	 * CRC32算法
	 */
	public static int CRC32Hash(String key) {
		CRC32 crc32 = new CRC32();
		for (int i = 0; i < key.length(); i++)
			crc32.update(key.charAt(i));
		int crc = (int) crc32.getValue();
		return (crc >> 16) & 0x7fff;
	}

	/**
	 * Calculates the ketama hash value for a string
	 * 
	 * @param s
	 * @return
	 */
	public static long MD5Hashing(String key) {
		MessageDigest md5 = null;
		try {
			md5 = MessageDigest.getInstance("MD5");
		} catch (NoSuchAlgorithmException e) {
		}
		md5.reset();
		md5.update(key.getBytes());
		byte[] bKey = md5.digest();
		long res = ((long) (bKey[3] & 0xFF) << 24)
				| ((long) (bKey[2] & 0xFF) << 16)
				| ((long) (bKey[1] & 0xFF) << 8) | (long) (bKey[0] & 0xFF);
		return res;
	}

	/**
	 * JAVA自己带的算法
	 */
	public static int java(String key) {
		int h = 0;
		int off = 0;
		int len = key.length();
		for (int i = 0; i < len; i++) {
			h = 31 * h + key.charAt(off++);
		}
		return h;
	}

	/**
	 * 混合hash算法，输出64位的值
	 */
	public static long mixHash(String key) {
		long hash = key.hashCode();
		hash <<= 32;
		hash |= FNVHash1(key);
		return hash;
	}
}